/*=============================================================================   
#     FileName: ffmpeg_encoder.c   
#         Desc: an example of ffmpeg fileter  
#       Author: licaibiao   
#   LastChange: 2017-03-18    
=============================================================================*/

#include <stdio.h>
extern "C"
{
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}

#define ERR(x)                           \
    if (!(x))                            \
    {                                    \
        printf("%d failed\n", __LINE__); \
        exit(-1);                        \
    }

#define PL                           \
    printf("%d failed\n", __LINE__); \
    exit(-1);

const int width = 640;
const int height = 480;
const int max_len = width * height * 2;

void pgm_save(unsigned char *buf, int wrap, int xsize, int ysize, char *filename)
{
    FILE *f;
    int i;
    f = fopen(filename, "wb");
    fprintf(f, "P5\n%d %d\n%d\n", xsize, ysize, 255);
    for (i = 0; i < ysize; ++i)
    {
        fwrite(buf + i * wrap, 1, xsize, f);
    }
    fclose(f);
}

void decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt, char *filename)
{
    char buf[1024];
    int ret;
    ret = avcodec_send_packet(dec_ctx, pkt);
    ERR(ret < 0);
    while (ret >= 0)
    {
        ret = avcodec_receive_frame(dec_ctx, frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
        {
            return;
        }
        else if (ret < 0)
        {
            PL;
        }
        fflush(stdout);
        snprintf(buf, sizeof(buf), "%s-%d", filename, dec_ctx->frame_number);
        pgm_save(frame->data[0], frame->linesize[0], frame->width, frame->height, buf);
    }
}

int main(int argc, char **argv)
{
    if (argc < 3)
    {
        printf("ge 2 par\n");
        return -1;
    }
    char *filename = argv[1], *outfile = argv[2];
    AVCodec *codec;
    AVCodecParserContext *parser;
    AVCodecContext *c = nullptr;
    FILE *f;
    AVFrame *frame;
    uint8_t inbuf[max_len + AV_INPUT_BUFFER_PADDING_SIZE] = {0};
    uint8_t *data = nullptr;
    size_t data_size = 0;
    int ret;
    AVPacket *pkt;
    pkt = av_packet_alloc();
    codec = (AVCodec *)avcodec_find_decoder((AVCodecID)atoi(argv[3]));
    if (!codec)
    {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }

    parser = av_parser_init(codec->id);
    if (!parser)
    {
        printf("av_parser_init failed.\n");
        exit(1);
    }

    c = avcodec_alloc_context3(codec);
    ERR(c);

    if (avcodec_open2(c, codec, nullptr) < 0)
    {
        printf("%d failed\n", __LINE__);
        exit(-1);
    }
    frame = av_frame_alloc();
    ERR(frame);
    f = fopen(filename, "rb");
    ERR(f);

    while (!feof(f))
    {
        data_size = fread(inbuf, 1, max_len, f);
        ERR(data_size);
        data = inbuf;
        while (data_size > 0)
        {
            ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size, data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
            ERR(ret < 0);
            data += ret;
            data_size -= ret;
            if (pkt->size)
            {
                decode(c, frame, pkt, outfile);
            }
        }
    }
    decode(c, frame, nullptr, outfile);
    fclose(f);
    av_parser_close(parser);
    avcodec_free_context(&c);
    av_frame_free(&frame);
    av_packet_free(&pkt);

    return 0;
}

//#define ENCODE_YUV

// #ifdef ENCODE_YUV
// #define INPUT_FILE "yuv_512_288.yuv"
// #define INPUT_WIDTH 512
// #define INPUT_HEIGHT 288
// #else
// #define INPUT_FILE "yuyv_640_480.yuyv"
// #define INPUT_WIDTH 640
// #define INPUT_HEIGHT 480
// #endif

// #define OUTPUT_FILE "ds.h264"
// #define FRAMENUM 300

// int flush_encoder(AVFormatContext *fmt_ctx, unsigned int stream_index)
// {
//     int ret;
//     int got_frame;
//     AVPacket enc_pkt;
//     // if (!(fmt_ctx->streams[stream_index]->video_codec->capabilities & CODEC_CAP_DELAY))
//     //     return 0;
//     while (1)
//     {
//         enc_pkt.data = NULL;
//         enc_pkt.size = 0;
//         av_init_packet(&enc_pkt);
//         ret = avcodec_encode_video2(fmt_ctx->streams[stream_index]->codec, &enc_pkt, NULL, &got_frame);
//         av_frame_free(NULL);
//         if (ret < 0)
//             break;
//         if (!got_frame)
//         {
//             ret = 0;
//             break;
//         }
//         printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n", enc_pkt.size);
//         /* mux encoded frame */
//         ret = av_write_frame(fmt_ctx, &enc_pkt);
//         if (ret < 0)
//             break;
//     }
//     return ret;
// }

// int main(int argc, char *argv[])
// {
//     AVFormatContext *pFormatCtx;
//     AVOutputFormat *fmt;
//     AVStream *video_st;
//     AVCodecContext *pCodecCtx;
//     AVCodec *pCodec;
//     AVPacket pkt;
//     uint8_t *picture_buf;
//     AVFrame *pFrame;
//     int picture_size;
//     int y_size;
//     int framecnt = 0;
//     FILE *in_file;                               //Input raw YUV data
//     int in_w = INPUT_WIDTH, in_h = INPUT_HEIGHT; //Input data's width and height
//     int framenum = FRAMENUM;                     //Frames to encode
//     const char *out_file = OUTPUT_FILE;

//     int i, j, k;
//     int num;
//     int index_y, index_u, index_v;
//     uint8_t *y_, *u_, *v_, *in;

//     int got_picture = 0;
//     int ret;

//     in_file = fopen(INPUT_FILE, "rb"); //Input raw YUV data
//     av_register_all();
//     //Method1.
//     pFormatCtx = avformat_alloc_context();
//     //Guess Format
//     fmt = av_guess_format(NULL, out_file, NULL);
//     pFormatCtx->oformat = fmt;

//     //Method 2.
//     /* 初始化输出码流的AVFormatContext */
//     //avformat_alloc_output_context2(&pFormatCtx, NULL, NULL, out_file);
//     //fmt = pFormatCtx->oformat;
//     //Open output URL
//     if (avio_open(&pFormatCtx->pb, out_file, AVIO_FLAG_READ_WRITE) < 0)
//     {
//         printf("Failed to open output file! \n");
//         return -1;
//     }

//     /* 创建输出码流的AVStream */
//     video_st = avformat_new_stream(pFormatCtx, 0);
//     //video_st->time_base.num = 1;
//     //video_st->time_base.den = 25;

//     if (video_st == NULL)
//     {
//         return -1;
//     }
//     //Param that must set
//     pCodecCtx = video_st->codec;
//     //pCodecCtx->codec_id =AV_CODEC_ID_HEVC;
//     pCodecCtx->codec_id = fmt->video_codec;
//     pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
// #ifdef ENCODE_YUV
//     pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
// #else
//     pCodecCtx->pix_fmt = AV_PIX_FMT_YUV422P;
// #endif
//     pCodecCtx->width = in_w;
//     pCodecCtx->height = in_h;
//     pCodecCtx->bit_rate = 2000000;
//     pCodecCtx->gop_size = 10;

//     pCodecCtx->time_base.num = 1;
//     pCodecCtx->time_base.den = 25;

//     //H264
//     //pCodecCtx->me_range = 16;
//     //pCodecCtx->max_qdiff = 4;
//     //pCodecCtx->qcompress = 0.6;
//     pCodecCtx->qmin = 10;
//     pCodecCtx->qmax = 51;

//     //Optional Param
//     pCodecCtx->max_b_frames = 3;

//     // Set Option
//     AVDictionary *param = 0;
//     //H.264
//     if (pCodecCtx->codec_id == AV_CODEC_ID_H264)
//     {
//         av_dict_set(&param, "preset", "slow", 0);
//         /* 这个可以让libav不缓存视频帧 */
//         av_dict_set(&param, "tune", "zerolatency", 0);
//         //av_dict_set(&param, "profile", "main", 0);
//     }

//     //Show some Information
//     av_dump_format(pFormatCtx, 0, out_file, 1);

//     /* 查找编码器 */
//     pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
//     if (!pCodec)
//     {
//         printf("Can not find encoder! \n");
//         return -1;
//     }

//     /* 打开编码器 */
//     if (avcodec_open2(pCodecCtx, pCodec, &param) < 0)
//     {
//         printf("Failed to open encoder! \n");
//         return -1;
//     }

//     pFrame = av_frame_alloc();
//     picture_size = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
//     picture_buf = (uint8_t *)av_malloc(picture_size);

// #ifdef ENCODE_YUV
//     avpicture_fill((AVPicture *)pFrame, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
// #else
//     pFrame->format = pCodecCtx->pix_fmt;
//     pFrame->width = pCodecCtx->width;
//     pFrame->height = pCodecCtx->height;
//     av_image_alloc(pFrame->data, pFrame->linesize, pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, 32);
// #endif

//     //Write File Header
//     /* 写文件头（对于某些没有文件头的封装格式，不需要此函数。比如说MPEG2TS） */
//     avformat_write_header(pFormatCtx, NULL);

//     /* Allocate the payload of a packet and initialize its fields with default values.  */
//     av_new_packet(&pkt, picture_size);

//     y_size = pCodecCtx->width * pCodecCtx->height;
//     for (j = 0; j < framenum; j++)
//     {
//         //Read raw YUV data
// #ifdef ENCODE_YUV
//         if (fread(picture_buf, 1, y_size * 3 / 2, in_file) <= 0)
//         {
// #else
//         if (fread(picture_buf, 1, y_size * 2, in_file) <= 0)
//         {
// #endif
//             printf("Failed to read raw data! \n");
//             return -1;
//         }
//         else if (feof(in_file))
//         {
//             break;
//         }

// #ifdef ENCODE_YUV
//         pFrame->data[0] = picture_buf;                  // Y
//         pFrame->data[1] = picture_buf + y_size;         // U
//         pFrame->data[2] = picture_buf + y_size * 5 / 4; // V
// #else
//         num = y_size * 2 - 4;
//         index_y = 0;
//         index_u = 0;
//         index_v = 0;
//         y_ = pFrame->data[0];
//         u_ = pFrame->data[1];
//         v_ = pFrame->data[2];
//         in = picture_buf;

//         for (i = 0; i < num; i = i + 4)
//         {
//             *(y_ + (index_y++)) = *(in + i);
//             *(u_ + (index_u++)) = *(in + i + 1);
//             *(y_ + (index_y++)) = *(in + i + 2);
//             *(v_ + (index_v++)) = *(in + i + 3);
//         }
// #endif
//         //PTS
//         //pFrame->pts=i;
//         pFrame->pts = j * (video_st->time_base.den) / ((video_st->time_base.num) * 25);

//         //Encode
//         /* 编码一帧视频。即将AVFrame（存储YUV像素数据）编码为AVPacket（存储H.264等格式的码流数据） */
//         int ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_picture);
//         if (ret < 0)
//         {
//             printf("Failed to encode! \n");
//             return -1;
//         }
//         if (got_picture == 1)
//         {
//             //printf("Succeed to encode frame: %5d\tsize:%5d\n",framecnt,pkt.size);
//             framecnt++;
//             pkt.stream_index = video_st->index;

//             /* 将编码后的视频码流写入文件 */
//             ret = av_write_frame(pFormatCtx, &pkt);

//             av_free_packet(&pkt);
//         }
//     }
//     //Flush Encoder
//     /* 输入的像素数据读取完成后调用此函数。用于输出编码器中剩余的AVPacket */
//     ret = flush_encoder(pFormatCtx, 0);
//     if (ret < 0)
//     {
//         printf("Flushing encoder failed\n");
//         return -1;
//     }

//     //Write file trailer
//     /* 写文件尾（对于某些没有文件头的封装格式，不需要此函数。比如说MPEG2TS） */
//     av_write_trailer(pFormatCtx);

//     //Clean
//     if (video_st)
//     {
//         avcodec_close(video_st->codec);
//         av_free(pFrame);
//         av_free(picture_buf);
//     }
//     avio_close(pFormatCtx->pb);
//     avformat_free_context(pFormatCtx);

//     fclose(in_file);

//     return 0;
// }